#include "stdafx.h"
#include "urlparse.h"
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#define ALLOC(m,s) \
         do {  \
            m = (char*) malloc (s); \
            if (!m) {   \
               return E_INSUFFICIENT_MEMORY; \
            }   \
         } while (0);


CURLParser::CURLParser()
{
   m_scheme = NULL;
   m_domain = NULL;
   m_port = 0;
   m_path = NULL;
   m_anchor = NULL;
   m_url = NULL;
}

CURLParser::~CURLParser()
{
   FreeMem();
}

int CURLParser::Parse(const char* url)
{
   int ret;
   const char* url_non_space = NULL;
   char const* domain = NULL;
   char const* port = NULL;
   char const* anchor = NULL;
   char const* path = NULL;
   
   FreeMem();
   
   url_non_space = GetFirstNonSpace(url);
   ALLOC (m_url, strlen (url_non_space) +1 );
   strcpy (m_url, url_non_space);

   ret = GetScheme(url_non_space, &domain);
   if (ret != 0) {
      return ret;
   }
   
   assert (domain);
   ret = GetDomain(domain, &port);
   if (ret != 0) {
      return ret;
   }
   
   if (*port != ':') {
      return GetPathAndAnchor(port);
   }
   
   ret = GetPort(port, &path);
   if (ret != 0) {
      return ret;
   }
   
   return GetPathAndAnchor(path);
}

#define SCHEME_DELIMITER "://"


int CURLParser::GetScheme(const char* url, char const ** domain)
{
   assert (url);
   assert (domain);
   assert (!m_scheme);
      
   const char* found = strstr (url, SCHEME_DELIMITER);
   if (!found) {
      *domain = url; 
      return SetDefaultScheme();
   }

   int count = found - url + 1;
   ALLOC (m_scheme, count);
   m_scheme[count-1] = 0;
   for (int i = 0; i < count-1; i++) {
      m_scheme[i] = url[i];
   }
   *domain = found + strlen(SCHEME_DELIMITER);
   return 0;
}

#define DEFAULT_SCHEME "http"

int CURLParser::SetDefaultScheme()
{
   assert (!m_scheme);
   
   ALLOC (m_scheme, sizeof(DEFAULT_SCHEME));
   
   strcpy (m_scheme, DEFAULT_SCHEME);
   return 0;
}

int CURLParser::GetDomain (const char* domain, char const ** port)
{
   assert (domain);
   assert (port);
   assert (!m_domain);
   
   int slash_pos = -1;
   int colon_pos = -1;

   int i = 0;
   while (domain[i] != '\0' && domain[i] != '/' && domain[i] != ':') {
      i++;
   }

   *port = domain + i;
   int count = *port - domain + 1;
   ALLOC (m_domain, count);
   m_domain[count-1] = 0;
   for (i = 0; i < count-1; i++) {
      m_domain[i] = domain[i];
   }
   return 0;
}

#define IS_DIGIT(x) (x>='0' && x<='9')

int CURLParser::GetPort(const char* port, char const ** path)
{
   assert (port);
   assert (path);
   
   assert (*port == ':');
   m_port = 0;
   const char* c = port+1;
   bool numeric = false;
   while (IS_DIGIT(*c)) {
      numeric = true;
      m_port = m_port *10 + *c - '0';
      c++;
   }
   *path = c;
   if (!numeric) {
      return E_ILLEGAL_URL;
   }
   return 0;
}

#define ANCHOR_DELIMITER '#'
#define ROOT_DIR "/"

int CURLParser::GetPathAndAnchor(const char* path)
{
   assert (path);
   assert (!m_path);
   assert (!m_anchor);
   
   if (*path == '\0') {
      ALLOC (m_path, sizeof(ROOT_DIR));
      strcpy (m_path, ROOT_DIR);
      if (m_anchor) {
         free (m_anchor);
      }
      return 0;
   }

   char* found = strchr(path, ANCHOR_DELIMITER);
   if (found) {
      ALLOC (m_anchor, strlen(found+1) +1 );
      strcpy (m_anchor, found+1);
   }

   int count;
   if (found) {
      count = found - path +1;
   } else {
      count = strlen(path) + 1;
   }

   ALLOC (m_path, count);
   m_path[count-1] = '\0';
   for (int i = 0; i < count-1; i++) {
      m_path [i] = path[i];
   }
    
   return 0;
}

void CURLParser::FreeMem()
{
   if (m_scheme) {
      free (m_scheme);
      m_scheme = NULL;
   }
   if (m_domain) {
      free (m_domain);
      m_domain = NULL;
   }
   if (m_path) {
      free (m_path);
      m_path = NULL;
   }
   if (m_anchor) {
      free (m_anchor);
      m_anchor = NULL;
   }
   if (m_url) {
       free (m_url);
       m_url = NULL;
   }
}

const char* GetFirstNonSpace(const char* s)
{
   const char* p = s;
   while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') {
      p++;
   }
   return p;
}

